﻿package com.yc.baihe.dao;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Blob;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;

import com.yc.baihe.utils.LogUtil;


public class DBHelper {
	private static Connection con=null;
	private static PreparedStatement pstmt=null;
	private static ResultSet rs=null;
	
	/**
	 * 数据库驱动的加载
	 */
//	static{
//		try{
//			Class.forName(ReadDBProperties.getInstance().getProperty("drivername"));
//		}catch(ClassNotFoundException e){
//			//e.printStackTrace();
//			LogUtil.log.error(e.toString());//记录到日志文件中
//			throw new RuntimeException();//抛出一个运行时异常,以便调用者处理这个异常
//		}
//	}
//	
	/**
	 * 获取连接的方法
	 * @return
	 */
//	public static Connection getConnection(){
//		try {
//			con=DriverManager.getConnection(ReadDBProperties.getInstance().getProperty("url"),ReadDBProperties.getInstance());
//		} catch (SQLException e) {
//			//e.printStackTrace();
//			LogUtil.log.error(e.toString());
//			throw new RuntimeException();
//		}
//		return con;
//	}
	
	public Connection getConnection(){
		try {
			Context ctxt=new InitialContext();
			DataSource ds=(DataSource) ctxt.lookup("java:comp/env/jdbc");
			con=ds.getConnection();
		} catch (NamingException e) {
			LogUtil.log.error(e.toString());
			throw new RuntimeException();
		} catch (SQLException e) {
			LogUtil.log.error(e.toString());
			throw new RuntimeException();
		}
		return con;
	}
	
	
	/**
	 * 给占位符赋值的方法
	 * @param pstmt:要赋值的预编译
	 * @param params:对应的占位符的值得集合
	 * @throws SQLException 
	 */
	public  void setValues(PreparedStatement pstmt,List<Object> params) throws SQLException{
		//判断params是否为空，如果不为空说明用户给定的sql语句中含有占位符 ?
		if(params!=null && params.size()>0){
			Object obj=null;
			for(int i=0;i<params.size();i++){
				obj=params.get(i);
				if(obj!=null){
					try {
						if("[B".equals(obj.getClass().getName())){
							pstmt.setBytes(i+1, (byte[])obj);
						}else{
							pstmt.setString(i+1, String.valueOf( obj ) );
						}
				
					} catch (SQLException e) {
						//e.printStackTrace();
						LogUtil.log.error(e.toString());
					}
				}else{
					pstmt.setString(i+1, String.valueOf( obj ) );
				}
			}
		}
	}
	
	/**
	 * 更新数据的方法
	 * @param sql:要执行的查询语句
	 * @param params:对应的sql语句中问号的值
	 * @return:语句执行后，影响数据的行数
	 */
	
	public int update(String sql,List<Object> params){
		con=this.getConnection();//获取一个连接
		int result=0;
		
		try {
			pstmt=con.prepareStatement(sql);//预编译sql语句
			
//			//判断params是否为空，如果不为空说明用户给定的sql语句中含有占位符 ?
//			if(params!=null && params.size()>0){
//				for(int i=0;i<params.size();i++){
//					pstmt.setString(i+1, String.valueOf(params.get(i) ) );
//				}
//			}
			this.setValues(pstmt, params);
			//执行
			result=pstmt.executeUpdate();
		} catch (SQLException e) {
			//e.printStackTrace();
			LogUtil.log.error(e.toString());
			throw new RuntimeException();
		} finally{
			this.close();//关闭打开的资源
		}
		return result;
	}
	
	/**
	 * 带事务的更新
	 * @param sqls:要执行的查询语句
	 * @param params:对应的sql语句中的占位符的值
	 * @return:成功返回true
	 */
	public boolean update(List<String>sqls,List<List<Object>>params){
		con=this.getConnection();//获取一个连接
		//java是自动提交事务的,所以我们必须先关闭自动提交
		try {
			con.setAutoCommit(false);
			//循环执行sql语句
			for(int i=0;i<sqls.size();i++){
				pstmt=con.prepareStatement(sqls.get(i));//取出第i条sql语句
				this.setValues(pstmt, params.get(i));//取出第i条sql语句对应的参数列表
				pstmt.executeUpdate();
			}
			//如果所有语句执行后都没有出错，则提交
			con.commit();
		} catch (SQLException e) {
			//如果执行过程中出错了，则回滚
			try {
				con.rollback();
			} catch (SQLException e1) {
				LogUtil.log.error(e.toString());
				throw new RuntimeException();
			}
			LogUtil.log.error(e.toString());
			throw new RuntimeException();
		}finally{
			try {
				con.setAutoCommit(true);
			} catch (SQLException e) {
				LogUtil.log.error(e.toString());
				throw new RuntimeException();
			}
			this.close();
		}
		return true;
	}
	
	public boolean updates(List<String>sqls,List<List<Object>>params){
		con=this.getConnection();//获取一个连接
		//java是自动提交事务的,所以我们必须先关闭自动提交
		try {
			con.setAutoCommit(false);
			//循环执行sql语句
			for(int i=0;i<sqls.size();i++){
				pstmt=con.prepareStatement(sqls.get(i));//取出第i条sql语句
				this.setValues(pstmt, params.get(i));//取出第i条sql语句对应的参数列表
				pstmt.addBatch();
			}
			//如果所有语句执行后都没有出错，则提交
			pstmt.executeBatch();
			con.commit();
		} catch (SQLException e) {
			//如果执行过程中出错了，则回滚
			try {
				con.rollback();
			} catch (SQLException e1) {
				LogUtil.log.error(e.toString());
				throw new RuntimeException();
			}
			LogUtil.log.error(e.toString());
			throw new RuntimeException();
		}finally{
			try {
				con.setAutoCommit(true);
			} catch (SQLException e) {
				LogUtil.log.error(e.toString());
				throw new RuntimeException();
			}
			this.close();
		}
		return true;
	}
	/**
	 * 查询数据的方法
	 * @param sql:要执行的查询语句
	 * @param params:对应的sql语句中问号的值
	 * @return:所有满足条件的数据的集合 Map<String,Object> key为列名
	 */
	public List<Map<String, Object>> find(String sql,List<Object> params){
		List<Map<String,Object>> result=new ArrayList<Map<String,Object>>();
		BufferedInputStream bis=null;
		
		try {
			//获取连接
			con=this.getConnection();
			
			//预编译语句
			pstmt=con.prepareStatement(sql);
			
			//给占位符赋值
			this.setValues(pstmt, params);
			
			//执行语句并获取返回的结果
			ResultSet rs=pstmt.executeQuery();
			
			//获取返回的结果集中列的信息
			ResultSetMetaData rsmd=rs.getMetaData();
			
			String[] cols=new String[rsmd.getColumnCount()];
			for(int i=0;i<cols.length;i++){//循环获取每个列的列名放到数组中
				cols[i]=rsmd.getColumnName(i+1);
			}
			
			HashMap<String, Object> map;//用来存放一条记录，以列名为空key,对应列的值为value
			String colType;
			Object obj;
			
			//因为封装在Map中，而map我们选用列名key，对应列的值为value，则我们需要获取所有的列名
			while(rs.next()){
				map=new HashMap<String,Object>();
				for(String col:cols){
					obj=rs.getObject(col);
					if(obj!=null){
						colType=obj.getClass().getName();
						//System.out.println(colType);
						if("oracle.sql.BLOB".equals(colType)){
							Blob bb=rs.getBlob(col);
							//System.out.println(bb);
							bis=new BufferedInputStream( bb.getBinaryStream());
							byte[] bt;
							
							bt=new byte[ (int) bb.length()];
							bis.read(bt);
							
							map.put(col, bt);
						}else{
							map.put(col, rs.getString(col));
						}
					}else{
						map.put(col, rs.getString(col));
					}
				}
				result.add(map);
			}
		} catch (SQLException e) {
			//e.printStackTrace();
			LogUtil.log.error(e.toString());
			throw new RuntimeException();
		}catch (IOException e){
			//e.printStackTrace();
			LogUtil.log.error(e.toString());
			throw new RuntimeException();
		} finally{
			if(bis!=null){
				try {
					bis.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			this.close();
		}
		return result;
	}
	
	/**
	 * 查询数据的方法
	 * @param sql: 要执行的查询语句
	 * @param params: 对应的sql语句中问号的值
	 * @return :所有满足条件的数据的集合  Map<String,Object> key为列名
	 */
	public List<List<String>> finds(String sql,List<Object> params){
		List<List<String>> results=new ArrayList<List<String>>();//存放所有的记录
		List<String> result;//存放一条记录
		try {
			//获取连接
			con=this.getConnection();
			
			//预编译语句
			pstmt=con.prepareStatement(sql);
			
			//给占位符赋值
			this.setValues(pstmt, params);
			
			//执行语句并获取返回的结果
			ResultSet rs=pstmt.executeQuery();
			
			//获取返回的结果集中列的信息
			ResultSetMetaData rsmd=rs.getMetaData();
			
			//因为封装在Map中，而map我们选用列名key，对应列的值为value，则我们需要获取所有的列名
			while(rs.next()){
				result=new ArrayList<String>();
				for(int i=0;i<rsmd.getColumnCount();i++){
					result.add(rs.getString(i+1));
				}
				results.add(result);
			}
		} catch (SQLException e) {
			//e.printStackTrace();
			LogUtil.log.error(e.toString());
			throw new RuntimeException();
		}
		return results;
	}
	

	/**
	 * 关闭资源的方法
	 */
	public  void close(){
		if(rs!=null){
			try {
				rs.close();
			} catch (SQLException e) {
				//e.printStackTrace();
				LogUtil.log.error(e.toString());
				throw new RuntimeException();
			}
		}
		
		if(pstmt!=null){
			try {
				pstmt.close();
			} catch (SQLException e) {
				//e.printStackTrace();
				LogUtil.log.error(e.toString());
				throw new RuntimeException();
			}
		}
		
		if(con!=null){
			try {
				con.close();
			} catch (SQLException e) {
				//e.printStackTrace();
				LogUtil.log.error(e.toString());
				throw new RuntimeException();
			}
		}
	}
	
	 /**
	  * 将数据库中的一条记录反射注入到一个类的对象中
	  * @param <T>:泛型
	  * @param sql：要执行的sql语句
	  * @param params：sql语句中对一个的占位符？的值
	  * @param c：要注入的类的信息
	  * @return
	  */
	public <T>List<T> find(String sql,List<Object> params,Class<T> c){
		List<T> list=new ArrayList<T>();
		
		try{
			con=this.getConnection();
			pstmt=con.prepareStatement(sql);
			setValues(pstmt, params);
			rs=pstmt.executeQuery();
			
			ResultSetMetaData rsmd=rs.getMetaData();	//获取结果集的元数据
			//从元数据中取出所有列的列名，存到一个数组
			String[] colName=new String[rsmd.getColumnCount()];
			for(int i=0;i<colName.length;i++){
				colName[i]=rsmd.getColumnName(i+1);
			}
			
			//获取给定的类的所有的方法
			Method[] methods=c.getMethods();
//			for(Method md:methods){
//				System.out.println(md.getName());
//			}
			
			T t=null;
			String methodName=null;	//方法名
			String col=null;	//列名
			String typeName=null; //类型名
			Object obj=null;
			
			while(rs.next()){
				//每循环一次就是一条记录，即一个对象
				t=c.newInstance();	//获取该类的一个实例  相当于new ..
				
				//循环取出所有的类名
				for(String cl:colName){ //USID
					//然后在列名前面加一个set   ->  setUSID   setUsid
					col="set"+cl;
					
					obj=rs.getObject(cl);//获取当前列中的数据的数据类型
					
					//循环所有的方法名，然后忽略大小写比较
					for(Method md:methods){
						methodName=md.getName();
						
						if(methodName.equalsIgnoreCase(col)){
							//setUsid()
							//获取当前循环的列的数据的类型
							if(obj!=null){
								typeName=obj.getClass().getName();
								if("java.lang.String".equals(typeName)){
									md.invoke(t, (String)obj);
								}else if("java.math.BigDecimal".equals(typeName)){
									try {
										md.invoke(t,Double.parseDouble(String.valueOf(obj)));
									} catch (Exception e) {
										md.invoke(t, rs.getInt(cl));
									}
								}else if("oracle.sql.CLOB".equals(typeName)){
									md.invoke(t, rs.getString(cl));
								}else if("java.sql.Date".equals(typeName)){
									md.invoke(t, rs.getString(cl));
								}else{
									md.invoke(t, (String)obj);
								}
							}else{
								md.invoke(t, (String)obj);
							}
							break;
						}
					}
				}
				//System.out.println(t);
				list.add(t);
			}
		}catch(SQLException e){
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		}
		return list;
		
	}
}
